using System;
using System.IO;
using System.Reflection;
using HashFoo.MigratorFoo;
using HashFoo.Nhib;
using HashFoo.NhibTests.DomainFixtures;
using HashFoo.SqlServer;
using NHibernate;

namespace HashFoo.NhibTests
{
    public class Helpers
    {
        public static string MockAssemblyPath
        {
            get { return Assembly.GetExecutingAssembly().GetCodeBaseAsLocalPath(); }
        }

        public static MigrationConfiguration MigrationConfiguration
        {
            get
            {
                return new MigrationConfiguration
                {
                    DbConfigAssemblyPath = MockAssemblyPath,
                    DownScriptsDirectory = Path.Combine(Assembly.GetExecutingAssembly().GetCodeBaseDir(), "Migrations\\Down"),
                    UpScriptsDirectory = Path.Combine(Assembly.GetExecutingAssembly().GetCodeBaseDir(), "Migrations\\Up"),
                    SchemaVersionTableName = "SchemaVersion"
                };
            }
        }

        public static MigrationManagerWorker CreateDefaultMigrationManagerWorker()
        {
            return new MigrationManagerWorker(MigrationConfiguration);
        }

        public static MigrationGenerator CreateDefaultMigrationGenerator()
        {
            return new MigrationGenerator()
                   {
                       Config = MockConfigurator.DefaultConfiguration,
                       MigrationInverter = new SimpleMigrationInverter()
                   };
        }

        public static void NukeDatabase()
        {
            DbTestingUtil.NukeMsSqlDatabase("HashFoo.Core.Tests");
        }

        /// <summary>
        /// Creates an in memory migration from the current domain model, nukes the test database, and applies the migration.
        /// </summary>
        /// <remarks>
        /// Basically, this is a way to create the database to exactly the current domain model without worrying about what the actaul
        /// current generated migrations are.
        /// </remarks>
        public static void ReBuildDatabase()
        {
            NukeDatabase();

            var migrationWorker = CreateDefaultMigrationManagerWorker();
            migrationWorker.SetActiveDbConfig("default");
            migrationWorker.Create();
        }

        public static void RunDbTest(Action<ISession> test, bool useTransaction = true)
        {
            if (useTransaction)
            {
                RunDbTestInTransaction(test);
                return;
            }

            try
            {
                using (var session = SessionFactory.OpenSession())
                {
                    test(session);

                    session.Close();
                }
            }
            finally
            {
                ReBuildDatabase();
            }
        }

        public static void RunDbTestInTransaction(Action<ISession> test)
        {
            using (var session = SessionFactory.OpenSession())
            using (var tx = new SessionTransaction(session))
            {
                test(session);

                tx.Dispose();
            }
        }

        static readonly Lazy<ISessionFactory> SessionFactoryBuilder =
            new Lazy<ISessionFactory>(() =>
            {
                ReBuildDatabase();
                var config = MockConfigurator.DefaultConfiguration;
                return config.BuildSessionFactory();
            });

        public static ISessionFactory SessionFactory
        {
            get { return SessionFactoryBuilder.Value; }
        }
    }
}